package fr.umlv.escapeir;

import java.awt.Graphics2D;
import java.util.ArrayList;

import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.Filter;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

import fr.umlv.escapeir.game.WorldActor;
import fr.umlv.escapeir.game.WorldContactListener;
import fr.umlv.escapeir.gesture.Gesture;
import fr.umlv.escapeir.gesture.GestureHandler;
import fr.umlv.escapeir.gesture.GestureListener;
import fr.umlv.escapeir.gesture.Masks;
import fr.umlv.escapeir.painters.Layer;
import fr.umlv.escapeir.painters.WorldGraphics2D;

public class GameWorld implements Layer, GestureListener {
	public static final int STEP_NUMBER = 60;
	public static final int WALL_INDEX = -2;

	private static final float TIME_STEP = 1.0f/STEP_NUMBER;
	private static final int VELOCITY_ITERATIONS = 6;
	private static final int POSITION_ITERATIONS = 2;

	private final World world;
	private final WorldGraphics2D world_painter;
	private final ArrayList<WorldActor> actors;
	private final Body walls[];
	private boolean paused;

	public GameWorld (int width, int height) {
		float heightOverThree = height/3;

		world = new World(new Vec2(0,0), true);
		final BodyDef wallBodyDef = new BodyDef();
		
		Filter filter = new Filter();
		filter.categoryBits = 0x0002;
		filter.groupIndex = -2;
		filter.maskBits = 0x0004;
		
		final FixtureDef wallFixtureDef = new FixtureDef();
		wallFixtureDef.restitution = 0;
		wallFixtureDef.friction = 1;
		wallFixtureDef.filter = filter;

		//Creating Ground
		final PolygonShape groundShape = new PolygonShape();
		groundShape.setAsEdge(new Vec2(0,0), new Vec2(width,0));

		wallBodyDef.position.set(0, 0);
		final Body groundBody = world.createBody(wallBodyDef);

		wallFixtureDef.shape = groundShape;
		groundBody.createFixture(wallFixtureDef);

		//Creating Ceiling
		final PolygonShape ceilShape = new PolygonShape();
		ceilShape.setAsEdge(new Vec2(0,0), new Vec2(width,0));

		wallBodyDef.position.set(0, heightOverThree);
		final Body ceilBody = world.createBody(wallBodyDef);

		wallFixtureDef.shape = ceilShape;
		Fixture f = ceilBody.createFixture(wallFixtureDef);

		//Creating left wall
		final PolygonShape leftWallShape = new PolygonShape();
		leftWallShape.setAsEdge(new Vec2(0,0), new Vec2(0, heightOverThree));

		wallBodyDef.position.set(0, 0);
		final Body leftWallBody = world.createBody(wallBodyDef);

		wallFixtureDef.shape = leftWallShape;
		leftWallBody.createFixture(wallFixtureDef);

		//Creating right wall
		final PolygonShape rightWallShape = new PolygonShape();
		rightWallShape.setAsEdge(new Vec2(0,0), new Vec2(0,heightOverThree));

		wallBodyDef.position.set(width, 0);
		final Body rightWallBody = world.createBody(wallBodyDef);

		wallFixtureDef.shape = rightWallShape;
		rightWallBody.createFixture(wallFixtureDef);

		world_painter = new WorldGraphics2D(0, height);
		actors = new ArrayList<>();
		walls = new Body[]{leftWallBody, rightWallBody, groundBody, ceilBody};
		paused = false;

		WorldContactListener.createForWorld(world);
		
//		world.setContactFilter(new ContactFilter () {
//			public boolean shouldCollide(Fixture fixtureA, Fixture fixtureB) {
//				System.out.println(fixtureA.getFilterData().groupIndex);
//				System.out.println(fixtureB.getFilterData().groupIndex);
//				if (fixtureA.getFilterData().groupIndex == fixtureB.getFilterData().groupIndex) {
//					if (fixtureA.getFilterData().groupIndex >= 0) return true;
//					else {
//						System.out.println("false");
//						return false;
//					}
//				}
//				return true;
//			}
//		});
		GestureHandler.self().registerListener(this);
	}

	public void render (Graphics2D painter) {
		world_painter.setPainter(painter);
		for (WorldActor struct : actors) {
			struct.render(world_painter);
		}
	}

	public Body addActor (WorldActor act, BodyDef bdef) {
		actors.add(act);
		return world.createBody(bdef);
	}

	public Body createBody(BodyDef bdef) {
		return world.createBody(bdef);
	}
	
	public void destroyActor (WorldActor act, Body b) {
		actors.remove(act);
		world.destroyBody(b);
	}
	
	public void destroyActor (WorldActor act, Body... bodies) {
		actors.remove(act);
		for (Body b : bodies) world.destroyBody(b);
	}

	public boolean step() {
		if (paused) return false;

		for(WorldActor st: actors) {
			st.apply();
		}
		world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
		return true;
	}

	public boolean isWall (Body wall) {
		for (Body b: walls) {
			if (b == wall) return true;
		}
		return false;
	}

	@Override
	public void gestureReceived(Gesture g) {
		if (Masks.SQUARE != g.getMask()) return;
		world.clearForces();
		paused = !paused;
	}
}
